package cn.wokoba.service.strategy.repository;


import cn.wokoba.dao.mapper.*;
import cn.wokoba.dao.pojo.*;
import cn.wokoba.dao.redis.RedissonService;
import cn.wokoba.service.strategy.model.*;
import cn.wokoba.service.strategy.rule.chain.factory.DefaultChainFactory;
import cn.wokoba.types.common.Constants;
import cn.wokoba.types.exception.AppException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.wokoba.types.enums.ResponseCode.UN_ASSEMBLED_STRATEGY_ARMORY;

@Slf4j
@Repository
public class StrategyRepository {
    @Autowired
    StrategyAwardMapper strategyAwardMapper;

    @Autowired
    StrategyMapper strategyMapper;

    @Autowired
    StrategyRuleMapper strategyRuleMapper;

    @Autowired
    RuleTreeMapper ruleTreeMapper;
    @Autowired
    RuleTreeNodeMapper ruleTreeNodeMapper;
    @Autowired
    RuleTreeNodeLineMapper ruleTreeNodeLineMapper;
    @Autowired
    RaffleActivityMapper raffleActivityMapper;
    @Autowired
    RaffleActivityCountDayMapper raffleActivityCountDayMapper;

    @Autowired
    RaffleActivityAccountMapper raffleActivityAccountMapper;

    @Autowired
    RedissonService redisService;


    public int getRateRange(Long strategyId) {
        return getRateRange(String.valueOf(strategyId));
    }

    public int getRateRange(String key) {
        String cacheKey = Constants.CacheKey.STRATEGY_RATE_RANGE_KEY + key;
        if (!redisService.isExists(cacheKey)) {
            throw new AppException(UN_ASSEMBLED_STRATEGY_ARMORY.getCode(), cacheKey + Constants.COLON + UN_ASSEMBLED_STRATEGY_ARMORY.getInfo());
        }
        return redisService.getValue(cacheKey);
    }

    public Integer getStrategyAwardAssemble(String key, Integer rateKey) {
        return redisService.getFromMap(Constants.CacheKey.STRATEGY_RATE_TABLE_KEY + key, rateKey);
    }

    public <K, V> void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map<K, V> strategyAwardSearchRateTable) {
        // 1. 存储抽奖策略范围值，如10000，用于生成1000以内的随机数
        redisService.setValue(Constants.CacheKey.STRATEGY_RATE_RANGE_KEY + key, rateRange);
        // 2. 存储概率查找表 - 存在则删除重新装配
        String tableCacheKey = Constants.CacheKey.STRATEGY_RATE_TABLE_KEY + key;
        if (redisService.isExists(tableCacheKey)) {
            redisService.remove(tableCacheKey);
        }
        Map<K, V> cacheRateTable = redisService.getMap(Constants.CacheKey.STRATEGY_RATE_TABLE_KEY + key);
        cacheRateTable.putAll(strategyAwardSearchRateTable);
    }

    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {
        // 优先从缓存获取
        String cacheKey = Constants.CacheKey.STRATEGY_AWARD_LIST_KEY + strategyId;
        List<StrategyAwardEntity> strategyAwardEntityList = redisService.getValue(cacheKey);
        if (null != strategyAwardEntityList && !strategyAwardEntityList.isEmpty()) return strategyAwardEntityList;
        // 从库中获取数据
        List<StrategyAward> strategyAwards = strategyAwardMapper.selectList(new LambdaQueryWrapper<StrategyAward>()
                .eq(StrategyAward::getStrategyId, strategyId));
        strategyAwardEntityList = strategyAwards.stream().map(strategyAward -> StrategyAwardEntity.builder()
                .strategyId(strategyAward.getStrategyId())
                .awardId(strategyAward.getAwardId())
                .awardTitle(strategyAward.getAwardTitle())
                .awardSubtitle(strategyAward.getAwardSubtitle())
                .awardCount(strategyAward.getAwardCount())
                .awardCountSurplus(strategyAward.getAwardCountSurplus())
                .awardRate(strategyAward.getAwardRate())
                .sort(strategyAward.getSort())
                .ruleModels(strategyAward.getRuleModels())
                .build()).toList();
        redisService.setValue(cacheKey, strategyAwardEntityList);
        return strategyAwardEntityList;
    }

    public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) {
        // 优先从缓存获取
        String cacheKey = Constants.CacheKey.STRATEGY_KEY + strategyId;
        StrategyEntity strategyEntity = redisService.getValue(cacheKey);
        if (null != strategyEntity) return strategyEntity;
        Strategy strategy = strategyMapper.selectOne(new LambdaQueryWrapper<Strategy>().eq(Strategy::getStrategyId, strategyId));
        if (null == strategy) return StrategyEntity.builder().build();
        strategyEntity = StrategyEntity.builder()
                .strategyId(strategy.getStrategyId())
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();
        redisService.setValue(cacheKey, strategyEntity);
        return strategyEntity;
    }

    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleWeight) {
        StrategyRule strategyRule = strategyRuleMapper.selectOne(new LambdaQueryWrapper<StrategyRule>()
                .eq(StrategyRule::getStrategyId, strategyId)
                .eq(StrategyRule::getRuleModel, ruleWeight));
        if (null == strategyRule) return null;
        return StrategyRuleEntity.builder()
                .strategyId(strategyRule.getStrategyId())
                .awardId(strategyRule.getAwardId())
                .ruleType(strategyRule.getRuleType())
                .ruleModel(strategyRule.getRuleModel())
                .ruleValue(strategyRule.getRuleValue())
                .ruleDesc(strategyRule.getRuleDesc())
                .build();
    }

    public void cacheStrategyAwardCount(String cacheKey, Integer awardCount) {
        if (redisService.isExists(cacheKey)) return;
        redisService.setAtomicLong(cacheKey, awardCount);
    }

    public Integer queryTodayUserRaffleCount(String userId, Long strategyId) {
        RaffleActivity raffleActivity = raffleActivityMapper.selectOne(new LambdaQueryWrapper<RaffleActivity>()
                .eq(RaffleActivity::getStrategyId, strategyId));
        RaffleActivityAccountDay raffleActivityAccountDay = raffleActivityCountDayMapper.selectOne(new LambdaQueryWrapper<RaffleActivityAccountDay>()
                .eq(RaffleActivityAccountDay::getActivityId, raffleActivity.getActivityId())
                .eq(RaffleActivityAccountDay::getUserId, userId)
                .eq(RaffleActivityAccountDay::getDay, new SimpleDateFormat("yyyy-MM-dd").format(new Date())));
        if (null == raffleActivityAccountDay) return 0;
        return raffleActivityAccountDay.getDayCount() - raffleActivityAccountDay.getDayCountSurplus();
    }



    public Boolean subtractionAwardStock(Long strategyId, Integer awardId, Date endDateTime) {
        String cacheKey = Constants.CacheKey.STRATEGY_AWARD_COUNT_KEY + strategyId + "_" + awardId;
        long surplus = redisService.decr(cacheKey);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            redisService.setAtomicLong(cacheKey, 0);
            return false;
        }
        // 1. 按照cacheKey decr 后的值，如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底，如果后续有恢复库存，手动处理等，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = cacheKey + Constants.UNDERLINE + surplus;
        Boolean lock = false;
        if (null != endDateTime) {
            long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
            lock = redisService.setNx(lockKey, expireMillis, TimeUnit.MILLISECONDS);
        } else {
            lock = redisService.setNx(lockKey);
        }
        if (!lock) {
            log.info("策略奖品库存加锁失败 {}", lockKey);
        }
        return lock;
    }

    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) {
        String cacheKey = Constants.CacheKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<StrategyAwardStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(strategyAwardStockKeyVO, 3, TimeUnit.SECONDS);
    }

    public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
        return queryStrategyRuleValue(strategyId, null, ruleModel);


    }

    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
        return strategyRuleMapper.selectOne(new LambdaQueryWrapper<StrategyRule>()
                .eq(StrategyRule::getStrategyId, strategyId)
                .eq(StrategyRule::getRuleModel, ruleModel)
                .eq(awardId != null, StrategyRule::getAwardId, awardId)).getRuleValue();
    }


    public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {
        // 优先从缓存获取
        String cacheKey = Constants.CacheKey.STRATEGY_AWARD_KEY + strategyId + Constants.UNDERLINE + awardId;
        StrategyAwardEntity strategyAwardEntity = redisService.getValue(cacheKey);
        if (null != strategyAwardEntity) return strategyAwardEntity;
        // 查询数据
        StrategyAward strategyAward = strategyAwardMapper.selectOne(new LambdaQueryWrapper<StrategyAward>()
                .eq(StrategyAward::getStrategyId, strategyId)
                .eq(StrategyAward::getAwardId, awardId));
        strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyAward.getStrategyId())
                .awardId(strategyAward.getAwardId())
                .awardTitle(strategyAward.getAwardTitle())
                .awardSubtitle(strategyAward.getAwardSubtitle())
                .awardCount(strategyAward.getAwardCount())
                .awardCountSurplus(strategyAward.getAwardCountSurplus())
                .awardRate(strategyAward.getAwardRate())
                .sort(strategyAward.getSort())
                .build();
        redisService.setValue(cacheKey, strategyAwardEntity);
        return strategyAwardEntity;
    }

    public StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId) {
        //select rule_models from strategy_award
        //        where strategy_id = #{strategyId} and award_id = #{awardId}
        String ruleModels = strategyAwardMapper.selectOne(new LambdaQueryWrapper<StrategyAward>()
                .eq(StrategyAward::getStrategyId, strategyId)
                .eq(StrategyAward::getAwardId, awardId)).getRuleModels();
        return StrategyAwardRuleModelVO.builder().ruleModels(ruleModels).build();
    }

    public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {
        // 优先从缓存获取
        String cacheKey = Constants.CacheKey.RULE_TREE_VO_KEY + treeId;
        RuleTreeVO ruleTreeVOCache = redisService.getValue(cacheKey);
        if (null != ruleTreeVOCache) return ruleTreeVOCache;

        // 从数据库获取
        RuleTree ruleTree = ruleTreeMapper.selectOne(Wrappers.<RuleTree>lambdaQuery()
                .eq(RuleTree::getTreeId, treeId));
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeMapper.queryRuleTreeNodeListByTreeId(treeId);
        List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineMapper.queryRuleTreeNodeLineListByTreeId(treeId);


        // 1. tree node line 转换Map结构
        Map<String, List<RuleTreeNodeLineVO>> ruleTreeNodeLineMap = ruleTreeNodeLines.stream()
                .map(ruleTreeNodeLine -> RuleTreeNodeLineVO.builder()
                        .treeId(ruleTreeNodeLine.getTreeId())
                        .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
                        .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
                        .ruleLimitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                        .ruleLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
                        .build())
                .collect(Collectors.groupingBy(RuleTreeNodeLineVO::getRuleNodeFrom)); // 根据 ruleNodeFrom 分组



        // 2. tree node 转换为Map结构
        Map<String, RuleTreeNodeVO> treeNodeMap = ruleTreeNodes.stream()
                .map(ruleTreeNode -> RuleTreeNodeVO.builder()
                        .treeId(ruleTreeNode.getTreeId())
                        .ruleKey(ruleTreeNode.getRuleKey())
                        .ruleDesc(ruleTreeNode.getRuleDesc())
                        .ruleValue(ruleTreeNode.getRuleValue())
                        .treeNodeLineVOList(ruleTreeNodeLineMap.get(ruleTreeNode.getRuleKey()))
                        .build())
                .collect(Collectors.toMap(RuleTreeNodeVO::getRuleKey, Function.identity())); // 使用 ruleKey 作为 Map 的 key

        // 3. 构建 Rule Tree
        RuleTreeVO ruleTreeVODB = RuleTreeVO.builder()
                .treeId(ruleTree.getTreeId())
                .treeName(ruleTree.getTreeName())
                .treeDesc(ruleTree.getTreeDesc())
                .treeRootRuleNode(ruleTree.getTreeRootRuleKey())
                .treeNodeMap(treeNodeMap)
                .build();
        redisService.setValue(cacheKey, ruleTreeVODB);
        return ruleTreeVODB;
    }


    public List<StrategyAwardEntity> queryRaffleStrategyAwardListByActivityId(Long activityId) {
        Long strategyId = raffleActivityMapper.selectOne(new LambdaQueryWrapper<RaffleActivity>()
                .eq(RaffleActivity::getActivityId, activityId)).getStrategyId();
        return queryStrategyAwardList(strategyId);
    }

    public Map<String, Integer> queryAwardRuleLockCount(String[] treeIds) {
        if (null == treeIds || treeIds.length == 0) return new HashMap<>();
        LambdaQueryWrapper<RuleTreeNode> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(RuleTreeNode::getRuleKey, "rule_lock")
                .in(RuleTreeNode::getTreeId, (Object[]) treeIds);
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeMapper.selectList(lambdaQueryWrapper);
        Map<String, Integer> resultMap = new HashMap<>();
        for (RuleTreeNode node : ruleTreeNodes) {
            String treeId = node.getTreeId();
            Integer ruleValue = Integer.valueOf(node.getRuleValue());
            resultMap.put(treeId, ruleValue);
        }
        return resultMap;
    }

    public Long queryStrategyIdByActivityId(Long activityId) {
        return raffleActivityMapper.selectOne(new LambdaQueryWrapper<RaffleActivity>()
                .eq(RaffleActivity::getActivityId, activityId)).getStrategyId();
    }

    public StrategyAwardStockKeyVO takeQueueValue() {
        String cacheKey = Constants.CacheKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
        return destinationQueue.poll();
    }

    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        strategyAwardMapper.updateStrategyAwardStock(strategyAward);
    }

    public List<RuleWeightVO> queryAwardRuleWeight(Long strategyId) {
        // 优先从缓存获取
        String cacheKey = Constants.CacheKey.STRATEGY_RULE_WEIGHT_KEY + strategyId;
        List<RuleWeightVO> ruleWeightVOS = redisService.getValue(cacheKey);
        if (null != ruleWeightVOS) return ruleWeightVOS;

        // 1. 查询权重规则配置
        String ruleValue = strategyRuleMapper.selectOne(Wrappers.<StrategyRule>lambdaQuery()
                .eq(StrategyRule::getStrategyId, strategyId)
                .eq(StrategyRule::getRuleModel, DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode())).getRuleValue();
        // 2. 借助实体对象转换规则
        StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
        strategyRuleEntity.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
        strategyRuleEntity.setRuleValue(ruleValue);
        Map<String, List<Integer>> ruleWeightValues = strategyRuleEntity.getRuleWeightValues();
        // 3. 遍历规则组装奖品配置
        ruleWeightVOS = ruleWeightValues.keySet().stream().map(ruleWeightKey -> {
            List<Integer> awardIds = ruleWeightValues.get(ruleWeightKey);
            // 也可以修改为一次从数据库查询
            List<RuleWeightVO.Award> awardList = awardIds.stream().map(awardId -> {
                StrategyAward strategyAward = strategyAwardMapper.selectOne(Wrappers.<StrategyAward>lambdaQuery()
                        .eq(StrategyAward::getStrategyId, strategyId)
                        .eq(StrategyAward::getAwardId, awardId));
                return RuleWeightVO.Award.builder()
                        .awardId(strategyAward.getAwardId())
                        .awardTitle(strategyAward.getAwardTitle())
                        .build();
            }).toList();
            return RuleWeightVO.builder()
                    .ruleValue(ruleValue)
                    .weight(Integer.valueOf(ruleWeightKey.split(Constants.COLON)[0]))
                    .awardIds(awardIds)
                    .awardList(awardList)
                    .build();
        }).toList();
        
        // 设置缓存
        redisService.setValue(cacheKey, ruleWeightVOS);
        return ruleWeightVOS;
    }

    public Integer queryActivityAccountTotalUseCount(String userId, Long strategyId) {
        Long activityId = raffleActivityMapper.selectOne(Wrappers.<RaffleActivity>lambdaQuery()
                .eq(RaffleActivity::getStrategyId,strategyId)).getActivityId();
        RaffleActivityAccount raffleActivityAccount = raffleActivityAccountMapper.selectOne(Wrappers.<RaffleActivityAccount>lambdaQuery()
                .eq(RaffleActivityAccount::getUserId, userId)
                .eq(RaffleActivityAccount::getActivityId, activityId));
        // 返回计算使用量
        return raffleActivityAccount.getTotalCount() - raffleActivityAccount.getTotalCountSurplus();
    }

    public <K, V> Map<K, V> getMap(String key) {
        return redisService.getMap(Constants.CacheKey.STRATEGY_RATE_TABLE_KEY + key);
    }

    public void cacheStrategyArmoryAlgorithm(String key, String beanName) {
        String cacheKey = Constants.CacheKey.STRATEGY_ARMORY_ALGORITHM_KEY + key;
        redisService.setValue(cacheKey, beanName);
    }

    public String queryStrategyArmoryAlgorithmFromCache(String key) {
        String cacheKey = Constants.CacheKey.STRATEGY_ARMORY_ALGORITHM_KEY + key;
        if (!redisService.isExists(cacheKey)) return null;
        return redisService.getValue(cacheKey);
    }
}
